Transposing F to C#: expressivity of parametric polymorphism in an object-oriented language

نویسندگان

  • Andrew Kennedy
  • Don Syme
چکیده

class Tree { } class Leaf : Tree { public A val; public Leaf(A a) { val = a; } } class Node : Tree { public Tree left; public Tree right; public Node(Tree l, Tree r) { left = l; right = r; } } interface Arrow { B apply(A x); } Copyright c © 2003 John Wiley & Sons, Ltd. Concurrency Computat.: Pract. Exper. 2003; 00:1–7 Prepared using cpeauth.cls TRANSPOSING F TO C] 3 class IntToString : Arrow { public string apply(int x) { return x.ToString(); } } class Mapper { public Tree tmap(Arrow f, Tree tree) { if (tree is Leaf) { return new Leaf(f.apply((tree as Leaf).val)); } else { Node node = tree as Node; return new Node(tmap(f, node.left), tmap(f, node.right)); } } } Tree itree = new Leaf(5); Tree stree = new Mapper().tmap(new IntToString(), itree); Observe that parameterized datatypes and their constructors are translated directly into parameterized classes, and polymorphic functions are translated into parameterized classes whose type parameters are the inferred type parameters of the function. Classes are required for parametric functions because, like any function value in core ML, these functions may be nested and contain free variables. Function values then correspond to object instances of such classes. One case in such translations requires careful attention: core ML permits let-bound functions to be polymorphic, and closure conversion must take into account the cases where the bodies of such functions contain free type variables, i.e. type variables that are not type parameters of the function. For example, the following function extracts integer and floating point values for each element of a list: fun markTwice (f:’a->int) (g:’a->real) (l:’a list) = let fun mark (h:’a->’b) = map h l in (mark f, mark g) end Here the function mark is inferred to have the type scheme ∀β.(α -> β) -> β list and is used with type arguments int and real. Conversion to parameterized classes must translate mark into a class parameterized by both the type parameter β and the free type variable α: class mark : Arrow,List> { List l; string apply(Arrow h) { ... } } class markTwice : ... { ... apply() { ... new Pair,List>( new mark(l).apply(f), new mark(l).apply(g)) ... } } Copyright c © 2003 John Wiley & Sons, Ltd. Concurrency Computat.: Pract. Exper. 2003; 00:1–7 Prepared using cpeauth.cls 4 A. J. KENNEDY AND D. SYME Such translations depend on the fact that in core ML polymorphic values are never passed around in their polymorphic form: instead they must be used locally at some (inferred) type instantiation. In this sense they are not “first-class” citizens of the language. From these observations we claim informally that core ML may be directly encoded into parameterized classes without using polymorphic methods. For example, it is possible to translate a toy fragment of ML into C with parameterized classes only. 1.2. First-class polymorphism We now present an example illustrating how adding polymorphic methods to a system with parameterized classes leads to a system whose polymorphism is more expressive than found in core ML. Consider the following fragment of C: interface IComparer { int Compare(T x, T y); } interface Sorter { void Sort(T[] a, IComparer c); } class QuickSort : Sorter { void Sort(T[] a, IComparer c) { ... } } class MergeSort : Sorter { void Sort(T[] a, IComparer c) { ... } } class IntComparer : IComparer { ... } class StringComparer : IComparer { ... } void Test(Sorter s, int[] ia, string[] sa) { s.Sort(ia, new IntComparer()); s.Sort(sa, new StringComparer()); } Here we define an interface∗ Sorter representing the type of polymorphic sorters, and whose single method is polymorphic in the element type of the array to be sorted. This interface is then implemented by particular sorter classes such as QuickSort and MergeSort. Object instances of such sorters can then be passed around at run-time and applied at different type instantiations, as shown in the method Test. We call this “first-class polymorphism”. Recent variants of Haskell [9, 13] and ML [17] support a similar notion. For example, the above example can be rendered in Russo’s extension to the module system of Standard ML. signature Sorter = sig val Sort : ’a array * (’a*’a->order) -> unit end structure QuickSort :> Sorter = struct ∗We could have used an abstract class in place of the interface Copyright c © 2003 John Wiley & Sons, Ltd. Concurrency Computat.: Pract. Exper. 2003; 00:1–7 Prepared using cpeauth.cls TRANSPOSING F TO C] 5 fun Sort(a, c) = ... end structure MergeSort :> Sorter = struct fun Sort(a, c) = ... end fun Test(s : [Sorter], ia, sa) = let structure S as Sorter = s in S.Sort(ia, Int.compare); S.Sort(sa, String.compare) end; Attempts to write the program in a similar fashion in Core ML fail, e.g. type ’a sorter = ’a array * (’a*’a->order) -> unit (* type error: sorter is applied to two different kinds of arrays *) fun test (sorter: ’a sorter, ia : int array, sa : string array) = ( sorter (ia, Int.compare); sorter (sa, String.compare) ) 2. System F with recursion The above examples motivate a more rigorous investigation into the expressivity of polymorphic methods. Our approach is to take a variant of the expressive calculus System F [15] and to show that it can be compiled to Generic C [10, 6], an object-oriented language with both parameterized classes and polymorphic methods. We focus on a translation which is fully type-preserving but which fails to support separate compilation, and briefly discuss a simpler translation which is only partially type-preserving but which supports separate compilation. We consider an extension of System F with recursion and a call-by-value evaluation order. Its syntax, typing rules and big-step evaluation semantics are presented in Figure 1. A typing environment Γ has the form Γ = X , x :A where free type variables in A are drawn from X . A typing judgment Γ ` M : A should be read “in the context of a typing environment Γ the term M has type A” with free type variables in M and A drawn from Γ. An evaluation judgment M ⇓ V should be read “closed term M evaluates to produce a closed value V ”. We identify types and terms up to renaming of bound variables, and assume that names of variables are chosen so as to be different from names already bound by Γ. The notation [B/X ]A denotes the capture-avoiding substitution of B for X in A; likewise for [B/A]M and [V /x ]M . Observe that • There are no base types. The usual System F encodings can be used to support types such as bool, nat and A list and operations over them (see [16] for proofs that these encodings are faithful). Copyright c © 2003 John Wiley & Sons, Ltd. Concurrency Computat.: Pract. Exper. 2003; 00:1–7 Prepared using cpeauth.cls 6 A. J. KENNEDY AND D. SYME

برای دانلود متن کامل این مقاله و بیش از 32 میلیون مقاله دیگر ابتدا ثبت نام کنید

ثبت نام

اگر عضو سایت هستید لطفا وارد حساب کاربری خود شوید

منابع مشابه

Transposing F to C (preliminary Report)

We present a type-preserving translation of the polymorphic lambda calculus (System F) into an extension of the C programming language supporting parameterized classes and polymorphic methods. We observe that whilst parameterized classes alone are sufficient to encode the parameterized datatypes and letpolymorphism of languages such as ML and Haskell, it is the presence of polymorphic virtual m...

متن کامل

F&<=: integrating parametric and "ad hoc" second order polymorphism

In the last years several object-oriented database systems have come to life. However among them there is a lack of statically strongly typed languages. This is a very important deeciency especially for languages, as the database programming languages, which are designed for complex applications of large size and that evolve in time. The absence of such a type discipline is justiied by the comp...

متن کامل

Declarative Semantics in Object-Oriented Software Development - A Taxonomy and Survey

One of the modern paradigms to develop an application is object oriented analysis and design. In this paradigm, there are several objects and each object plays some specific roles in applications. In an application, we must distinguish between procedural semantics and declarative semantics for their implementation in a specific programming language. For the procedural semantics, we can write a ...

متن کامل

A Program Transformation Technique to Support AOP within C++ Template

Aspect-oriented programming (AOP) provides assistance in modularizing concerns that crosscut the boundaries of system decomposition. Aspects have the potential to interact with many different kinds of language constructs in order to modularize crosscutting concerns. Although several aspect languages have demonstrated advantages in applying aspects to traditional modularization boundaries (e.g.,...

متن کامل

Comparing Haskell and Scala Support for Generic Programming

Datatype-generic programming involves parametrization of programs by the shape of data, in the form of type constructors such as ‘list of’. Most approaches to datatype-generic programming are developed in pure functional programming languages such as Haskell. We argue that the functional object-oriented language Scala is in many ways a better choice. Not only does Scala provide equivalents of a...

متن کامل

ذخیره در منابع من


  با ذخیره ی این منبع در منابع من، دسترسی به آن را برای استفاده های بعدی آسان تر کنید

برای دانلود متن کامل این مقاله و بیش از 32 میلیون مقاله دیگر ابتدا ثبت نام کنید

ثبت نام

اگر عضو سایت هستید لطفا وارد حساب کاربری خود شوید

عنوان ژورنال:
  • Concurrency - Practice and Experience

دوره 16  شماره 

صفحات  -

تاریخ انتشار 2004